﻿using SharpSoft.Geometries;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace SharpSoft.WPF.D3D
{
    /// <summary>
    /// 将2D几何图像生成3D模型
    /// </summary>
    public class Geometry2DTo3D : ModelVisual3D
    {

        public Geometry2DTo3D()
        {
            this.Transform = new ScaleTransform3D(1, -1, 1);
        }
         
        /// <summary>
        /// 几何形状的多边形近似中两点间距离上限。 较小的值生成更准确的结果，但会导致执行速度变慢。 如果 tolerance 小于.000001，则改用相反。
        /// </summary>
        public double Tolerance
        {
            get { return (double)GetValue(ToleranceProperty); }
            set { SetValue(ToleranceProperty, value); }
        }

        public static readonly DependencyProperty ToleranceProperty =
            DependencyProperty.Register("Tolerance", typeof(double), typeof(Geometry2DTo3D), new PropertyMetadata(0.1d));

        /// <summary>
        /// 原始2D几何图形
        /// </summary>
        public Geometry Geometry
        {
            get { return (Geometry)GetValue(GeometryProperty); }
            set { SetValue(GeometryProperty, value); }
        }

        public static readonly DependencyProperty GeometryProperty =
            DependencyProperty.Register("Geometry", typeof(Geometry), typeof(Geometry2DTo3D), new PropertyMetadata(null));
         
        /// <summary>
        /// 正面材质
        /// </summary>
        public Material Material
        {
            get { return (Material)GetValue(MaterialProperty); }
            set { SetValue(MaterialProperty, value); }
        }

        public static readonly DependencyProperty MaterialProperty =
            DependencyProperty.Register("Material", typeof(Material), typeof(Geometry2DTo3D), new PropertyMetadata(new DiffuseMaterial(Brushes.White)));
         
        /// <summary>
        /// 反面材质
        /// </summary>
        public Material BackMaterial
        {
            get { return (Material)GetValue(BackMaterialProperty); }
            set { SetValue(BackMaterialProperty, value); }
        }

        public static readonly DependencyProperty BackMaterialProperty =
            DependencyProperty.Register("BackMaterial", typeof(Material), typeof(Geometry2DTo3D), new PropertyMetadata(new DiffuseMaterial(Brushes.Gray)));
         
        /// <summary>
        /// 指定一个3D变换，使用该变换生成基于原始几何图形的背面
        /// </summary>
        public Transform3D BackTransform
        {
            get { return (Transform3D)GetValue(BackTransformProperty); }
            set { SetValue(BackTransformProperty, value); }
        }

        public static readonly DependencyProperty BackTransformProperty =
            DependencyProperty.Register("BackTransform", typeof(Transform3D), typeof(Geometry2DTo3D), new PropertyMetadata(new TranslateTransform3D(new Vector3D(0, 0, -5))));
         
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == GeometryProperty || e.Property == BackTransformProperty || e.Property == ToleranceProperty)
            {
                this.Create3DModel();
            }
            else if (e.Property == MaterialProperty)
            {
                if (this.Content is GeometryModel3D gm3)
                {
                    gm3.Material = this.Material;
                }
            }
            else if (e.Property == BackMaterialProperty)
            {
                if (this.Content is GeometryModel3D gm3)
                {
                    gm3.BackMaterial = this.BackMaterial;
                }
            }
        }
        /// <summary>
        /// 创建3D模型
        /// </summary>
        /// <returns></returns>
        protected virtual void Create3DModel()
        {
            if (this.Geometry == null)
            {
                this.Content = null;
                return;
            }
            Model3DGroup m3g = new Model3DGroup();

            var gm3d = new GeometryModel3D();
            var mesh = new MeshGeometry3D();
            gm3d.Material = this.Material;
            gm3d.BackMaterial = this.BackMaterial;
            gm3d.Geometry = mesh;
            m3g.Children.Add(gm3d);

            var pg = this.Geometry.GetFlattenedPathGeometry(this.Tolerance,
                                                ToleranceType.Relative);
            List<Point> list = new List<Point>();
            List<Polygon> polygons = new List<Polygon>();
            foreach (PathFigure fig in pg.Figures)
            {
                list.Clear();
                foreach (PathSegment seg in fig.Segments)
                {
                    if (seg is LineSegment lineseg)
                    {
                        list.Add(lineseg.Point);
                    }
                    else if (seg is PolyLineSegment polyline)
                    {
                        list.AddRange(polyline.Points);
                    }
                    else
                    {

                    }
                }
                List<Point2D> p2s = new List<Point2D>();
                foreach (var item in list)
                {
                    p2s.Add(new Point2D(item.X, item.Y));
                }
                Polygon polygon = new Polygon(p2s);
                polygons.Add(polygon);
            }

            var ps = Polygon.Combine(polygons);

            /**********注：经测试不应在此时处理2D坐标与3D坐标的差异，这会破坏原有的顶点顺序（方向）导致多边形三角化算法无法计算。
             * 应该在建模完成后再对模型进行变换，针对此实例的功能，默认将此实例的Transform设为Y轴方向缩放-1即可将字形转正。^_^***********/
            ProcessFigure(list, mesh, true);

            this.Content = m3g;
        }

        protected void ProcessFigure(List<Point> list, MeshGeometry3D mesh, bool isclosed)
        {
            var pcount = list.Count;//记录原始点数
            //if (isclosed)
            //{
            //    list.Add(list[0]);
            //}

            //int offset = mesh.Positions.Count;

            //var p3ds = new Point3D[list.Count];
            //for (int i = 0; i < list.Count; i++)
            //{//构造并添加所有顶点
            //    var p = list[i];
            //    var p3d = new System.Windows.Media.Media3D.Point3D(p.X, p.Y, 0);
            //    mesh.Positions.Add(p3d);//添加顶点 

            //    //var bp3d = this.BackTransform.Transform(p3d);
            //    //mesh.Positions.Add(bp3d);//添加反面顶点

            //}
            //int lastindex = pcount - 1;
            //for (int i = 0; i < pcount; i++)
            //{
            //    //侧边平面
            //    mesh.TriangleIndices.Add(i * 2);
            //    mesh.TriangleIndices.Add(i * 2 + 2);
            //    mesh.TriangleIndices.Add(i * 2 + 1);

            //    mesh.TriangleIndices.Add(i * 2 + 1);
            //    mesh.TriangleIndices.Add(i * 2 + 2);
            //    mesh.TriangleIndices.Add(i * 2 + 3);



            //    //正面 
            //    mesh.TriangleIndices.Add(i * 2);
            //    mesh.TriangleIndices.Add(i * 2 + 4);
            //    mesh.TriangleIndices.Add(i * 2 + 2);



            //    //底面 
            //    mesh.TriangleIndices.Add(i * 2 + 1);
            //    mesh.TriangleIndices.Add((lastindex - i) * 2 + 1);
            //    mesh.TriangleIndices.Add(i * 2 + 3);
            //}

            Point2D[] ps = new Point2D[list.Count];
            for (int i = 0; i < list.Count; i++)
            {
                var p = list[i];
                ps[i] = new Point2D(p.X, p.Y);
                mesh.Positions.Add(new System.Windows.Media.Media3D.Point3D(p.X, p.Y, 0));
            }
            List<int> VS = new List<int>();
         //   Triangulate.EarCut(new List<Point2D>(ps), VS);
            mesh.TriangleIndices = new Int32Collection(VS);
            //Polygon polygon = new Polygon(ps);
            //var pis = polygon.Triangulate();
            //for (int i = 0; i < pis.Count; i++)
            //{
            //    var p = pis[i];
            //    mesh.Positions.Add(new System.Windows.Media.Media3D.Point3D(p.X, p.Y, 0));
            //    mesh.TriangleIndices.Add(i);
            //}
            //  = new Int32Collection();
            mesh.TextureCoordinates.Add(new Point(0, 0));
            mesh.TextureCoordinates.Add(new Point(0, 1));
            mesh.TextureCoordinates.Add(new Point(1, 1));
            mesh.TextureCoordinates.Add(new Point(1, 0));
        }
    }
}
